package br.senai.sc.controller.business;

import java.math.BigDecimal;

import br.senai.sc.controller.exception.BusinessException;

/**
 * Contém os cálculos de IQA para o parâmetro Nitrogêneo.
 * 
 * @author Tiago
 */
public final class CalculoNitrogenioBusiness {

	/**
	 * Peso fixo utilizado para o cálculo da qualidade da Turbidez (qi^w)
	 */
	private static final double PESO = 0.10;

	/**
	 * Define o intervalo para o cálculo dos coliformes fecais. De 0 a 10.
	 */
	public static final int[] PRIMEIRO_INTERVALO = { 0, 10 };

	/**
	 * Define o intervalo para o cálculo dos coliformes fecais. De mais que 10 a
	 * 60.
	 */
	public static final int[] SEGUNDO_INTERVALO = { 10, 60 };

	/**
	 * Define o intervalo para o cálculo dos coliformes fecais. De mais que 60 a
	 * 90.
	 */
	public static final int[] TERCEIRO_INTERVALO = { 60, 90 };

	/**
	 * Utilizado para a quantidade de casas decimais devem ser utilizadas para o
	 * retorno do calculo de cada intervalo de valor.
	 */
	public static final int ESCALA = 10;

	/**
	 * Utilizado para a quantidade de casas decimais devem ser utilizadas para o
	 * cálculo de exponenciais.
	 */
	private static final int ESCALA_EXP = 20;

	private CalculoNitrogenioBusiness() {
	}

	/**
	 * Utilizado para fazer o cálculo da Nitrogêneo caso o valor informado é
	 * maior ou igual a 0 e menor ou igual a 10.
	 * 
	 * Cálculo realizado: -5,1*B15 + 100,17;
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor não esteja entre o intervalo correto
	 */
	private static BigDecimal primeiroIntervalo(BigDecimal valor)
			throws BusinessException {

		// se o valor for menor que 0 ou maior que 10 está incorreto
		if ((valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[0])) < 0)
				|| (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[1])) > 0)) {
			throw new BusinessException();
		}

		// SE(B15<=10; -5,1*B15 + 100,17;
		BigDecimal retorno = new BigDecimal("5.1").multiply(valor).negate() // -5,1*B15
				.add(new BigDecimal("100.17"));

		return retorno.setScale(ESCALA, IQABusiness.ROUND_MODE);
	}

	/**
	 * Utilizado para fazer o cálculo da Nitrogêneo caso o valor informado é
	 * maior do que 10 e menor ou igual a 60.
	 * 
	 * Cálculo realizado: -22,853*LN(B15) + 101,18;
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor não esteja entre o intervalo correto
	 */
	private static BigDecimal segundoIntervalo(BigDecimal valor)
			throws BusinessException {

		// se o valor for menor ou igual a 10 ou maior que 60 está incorreto
		if ((valor.compareTo(new BigDecimal(SEGUNDO_INTERVALO[0])) <= 0)
				|| (valor.compareTo(new BigDecimal(SEGUNDO_INTERVALO[1])) > 0)) {
			throw new BusinessException();
		}

		BigDecimal valorLN = new BigDecimal(Math.log(valor.doubleValue()))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);

		// SE(B15<=60; -22,853*LN(B15) + 101,18;
		BigDecimal retorno = new BigDecimal("22.853").multiply(valorLN)
				.negate() // -22,853*LN(B15)
				.add(new BigDecimal("101.18"));

		return retorno.setScale(ESCALA, IQABusiness.ROUND_MODE);
	}

	/**
	 * Utilizado para fazer o cálculo da Nitrogêneo caso o valor informado é
	 * maior do que 60 e menor ou igual a 90.
	 * 
	 * Cálculo realizado: 10000000000*B15^-5,1161
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor não esteja entre o intervalo correto
	 */
	private static BigDecimal terceiroIntervalo(BigDecimal valor)
			throws BusinessException {

		// se o valor for menor ou igual a 60 ou maior que 90 está incorreto
		if ((valor.compareTo(new BigDecimal(TERCEIRO_INTERVALO[0])) <= 0)
				|| (valor.compareTo(new BigDecimal(TERCEIRO_INTERVALO[1])) > 0)) {
			throw new BusinessException();
		}

		BigDecimal valorExp = new BigDecimal(Math.pow(valor.doubleValue(),
				-5.1161)).setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);

		// 10000000000*B15^-5,1161
		BigDecimal retorno = new BigDecimal(10000000000d).multiply(valorExp);

		return retorno.setScale(ESCALA, IQABusiness.ROUND_MODE);
	}

	/**
	 * Calcula o IQA do Nitrogêneo informado. Cálculo realizado: Se [valor] <
	 * que 0 Lança exceção Se [valor] <= 10
	 * 
	 * @see {@linkParametroNitrogenio#intervalo0a10(double)} Se [valor] <= 60
	 * @see {@link CalculoNitrogenioBusiness#segundoIntervalo(double)} Se
	 *      [valor] <= 90
	 * @see {@link CalculoNitrogenioBusiness#terceiroIntervalo(double)} Se
	 *      [valor] > 90 IQA = 1
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor informado seja inválido (Ex: menor que
	 *             0).
	 */
	public static BigDecimal calculaIQA(BigDecimal valor)
			throws BusinessException {

		// =SE(B15="";"NM";SE(B15<0;"ERR";
		if (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[0])) < 0) {
			throw new BusinessException();
		}

		// SE(B15<=10; -5,1*B15 + 100,17;
		if (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[1])) <= 0) {
			return primeiroIntervalo(valor);
		}

		// SE(B15<=60; -22,853*LN(B15) + 101,18;
		if (valor.compareTo(new BigDecimal(SEGUNDO_INTERVALO[1])) <= 0) {
			return segundoIntervalo(valor);
		}

		// SE(B15<=90; 10000000000*B15^-5,1161;
		if (valor.compareTo(new BigDecimal(TERCEIRO_INTERVALO[1])) <= 0) {
			return terceiroIntervalo(valor);
		}

		// SE(B15>90;1)
		// se o valor for maior que 90 sempre retorna 1
		return BigDecimal.ONE;
	}

	/**
	 * Calcula a média de qualidade (qi^w) do DBO.
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor informado seja inválido.
	 */
	protected static BigDecimal mediaQualidade(BigDecimal valor)
			throws BusinessException {
		return new BigDecimal(Math.pow(calculaIQA(valor).doubleValue(), PESO));
	}

}
